Skip to content

fix(adapters/hono): the auth wildcard yields paths the auth service does not own, and unbreak the #4116 ledger (#4117) - #4133

Merged
os-zhuang merged 1 commit into
mainfrom
claude/hono-static-discovery-hardcoded-2ysa9f
Jul 30, 2026
Merged

fix(adapters/hono): the auth wildcard yields paths the auth service does not own, and unbreak the #4116 ledger (#4117)#4133
os-zhuang merged 1 commit into
mainfrom
claude/hono-static-discovery-hardcoded-2ysa9f

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #4117并且修复 main 当前的红 —— 见下方第二节,那是我造成的合并竞态。

一、#4117 的修正

app.all(\${prefix}/auth/*`)声明整个命名空间,却是**终结式**的:无条件返回 auth service 的响应,**包括** better-auth 对它不实现的路径给出的 404,以及 legacyhandleAuthhandled: false` 时的那个 404。这就是 #4088 的形状 —— 由 #4116 的枚举扫出来,此前的手工 grep 没找到它。

现在 better-auth 返回 404、或 dispatcher 返回 handled: false,都视为"这条路径不属于本挂载",让路。判据优先用 dispatcher 自己的 handled 标志 —— 明确的所有权回答胜过从状态码推断;只有 better-auth 那一跳没有这个标志,那里用 404 作信号,与 #4092 一致。

这里的收益比 #4092 窄,代码里明说了:它不会让后注册的 Hono 路由变可达 —— 下方 ${prefix}/* 那个 dispatcher catch-all 是刻意终结式的(ADR-0076 OQ#9 / #3576 / #3608),无论如何都会吞掉它。真实收益是:未被认领的 /auth/* 路径现在会走到带闸门的 dispatch(),而不是在一个挂载之前就 404 收场 —— 于是为这类路径注册的 domain handler 变得可达,而这正是本适配器真正的扩展机制。

值得记一笔:我第一版测试是按 #4092 的结论写的,跑出来失败了 —— 让路之后接手的是那个 catch-all,不是后注册的路由。这个区别因此是被测出来的,不是我假设的。

二、修复 main 的红(我的竞态)

#4122 的 ledger 里登记了本文件两处 ratchet。而在它的 base(974c6d4)与合并之间,#4087 / #4112 落地并删除了 /storage/* —— squash 出来的 main 于是同时含有"我的 ledger 声明"和"已不存在的挂载",check:wildcard-fallthrough 在 main 上以 DECLARED but not found 失败。本 PR 删掉那条条目。

两点值得记录:

  1. 护栏表现正确 —— 是"声明了但扫不到"这条臂立刻抓到的,就在对新 main 的第一次运行上;
  2. fix(runtime,hono,plugin-dev): 退役 dispatcher 的 /storage bridge —— 它从来没按契约调用过 storage (#4087) #4112 独立地得出了同一判断,而且是从另一个方向:"the wildcard was wider than the two routes it served"。两次独立阅读撞上同一个缺陷,正是"枚举这个形状"而非"每次靠眼睛发现"的论据。

三、顺带修检查器的一个假阴性

callsContinuation 现在把"把续延交给 helper"(yieldUnowned(c, next, …))也算作让路。没有这条,检查器会把本 PR 的修正判成终结式 —— 一个假阴性,而且它会把 compose 的那个坑推向"在每个调用点重复一遍",而不是写一次。自检为此加了两例(交给 helper 算 / 既不调用也不传递不算)。

验证

结果
护栏 7 yielding / 0 ratcheted / 5 exempt(12 处挂载)—— ratchet 清零
--self-test 17 例
adapters/hono 全量 73 passed(新增 6)

两道防线都验过会咬:撤掉修正后,护栏报 TERMINAL 诊断,且新增测试 6 例里有 2 例失败。

关于既有噪声:本包有 3 个 tsc 错误与 2 个 eslint 规则定义错误(从仓库根调 eslint 时才显形,CI 的 ESLint job 在 #4122 上是绿的)。两边都量过,干净树与带改动完全一致。


Generated by Claude Code

…oes not own, and unbreak the #4116 ledger (#4117)

Two things, and the second is urgent.

## The fix (#4117)

`app.all(`${prefix}/auth/*`)` claimed a whole namespace and was TERMINAL: it
returned the auth service's response unconditionally, including better-auth's 404
for a path it does not implement, and the legacy `handleAuth` bridge's own
`handled: false` 404. That is #4088's shape. #4116's scan found it; the manual
greps that preceded that scan had not.

A 404 from better-auth, or `handled: false` from the dispatcher, now means "not
this mount's path" and the handler yields. The predicate is the dispatcher's OWN
`handled` flag wherever one exists — an explicit ownership answer beats inferring
one from a status; only the better-auth hand-off lacks such a flag, and there the
404 is the signal, exactly as in #4092.

What this buys here is narrower than #4092 and the code says so: it does NOT make
a later-registered Hono route reachable, because the `${prefix}/*` dispatcher
catch-all below is deliberately terminal (ADR-0076 OQ#9, #3576/#3608) and would
swallow one either way. It means an unowned `/auth/*` path now reaches that gated
`dispatch()` instead of dead-ending in a 404 built one mount earlier, so a domain
handler registered for it becomes reachable — which is this adapter's actual
extension mechanism. The first draft of the tests asserted the #4092 outcome and
failed, which is how the distinction got established rather than assumed.

## Unbreaking main

#4122 shipped the ledger declaring TWO ratcheted mounts here. Between its base
(974c6d4) and its merge, #4087/#4112 landed and DELETED the `/storage/*` bridge —
so the squash produced a main where the ledger declares a mount that no longer
exists, and `check:wildcard-fallthrough` fails on main with "DECLARED but not
found". That is my merge race, and this commit removes the entry.

Worth recording why the guard behaved well here: the stale-entry arm is what
caught it, immediately, on the first run against the new main. And #4112 reached
the same verdict about that wildcard independently, from the other direction —
"the wildcard was wider than the two routes it served". Two independent reads
landing on one defect is the argument for enumerating the shape rather than
finding it by eye each time.

Also extends `callsContinuation`: handing the continuation to a helper
(`yieldUnowned(c, next, …)`) now counts as yielding. Without that the checker
called this fix terminal — a false negative that would have pushed the compose
subtlety toward being duplicated at each call site instead of written once.

Guard: 7 yielding / 0 ratcheted / 5 exempt over 12 mounts. Self-test 17 cases.
adapters/hono 73 passed. Verified both defences bite: reverting the fix fails the
guard with the TERMINAL diagnostic and fails 2 of the 6 new tests. The 3 `tsc`
errors and 2 `eslint` rule-definition errors in this package are pre-existing,
measured identical on a clean tree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VZLsKypjrGhiLpio2uWKu
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 30, 2026 10:51am

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Jul 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): packages/adapters.

1 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/plugins/packages.mdx (via packages/adapters)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

adapters/hono 的 ${prefix}/auth/* 与 ${prefix}/storage/* 是终结式通配 —— 与 #4088 同一缺陷,只是这个包在仓内没有消费者

2 participants